/***************************************************************************
 *   Copyright (C) 2004 by Qball Cow	 		                   *
 *   e-mail: Qball@qballcow.nl 						   *
 *   Code is first based on work by Nicolas Sutre, 			   *
 *   but no original code is left                                          *
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 *   This program is distributed in the hope that it will be useful,       *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU General Public License for more details.                          *
 *                                                                         *
 *   You should have received a copy of the GNU General Public License     *
 *   along with this program; if not, write to the                         *
 *   Free Software Foundation, Inc.,                                       *
 *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
 ***************************************************************************/

/* TODO: 
 * 	  * Write console client
 * 	  * Write test program/script
 */


#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <usb.h>
#include "ueib.h"

#define STR_BUFF 	256
#define USB_DEBUG 	0

#define VENDOR_ID 	0x10cf
#define BASE_DEVICE_ID 	0x5500

int ueib_takeover_device(usb_dev_handle* udev,int interface )
{
	/* check if udev is defined */
	if(udev == NULL)
	{
		return -1;
	}

	/* set the usb debuf value */
	usb_set_debug(USB_DEBUG);

	if ( 0 > usb_detach_kernel_driver_np( udev, interface ) )
	{
		fprintf( stderr, "Disconnect OS driver: %s\n", usb_strerror());
	}                                                                  	
	else
	{
		fprintf( stderr, "Disconnected OS driver: %s\n", usb_strerror());	
	}
	if(usb_set_configuration(udev,1) < 0)
	{
		printf("Failed to set configuration\n");
		return -1;

	}

	/*claim interface */
	if(usb_claim_interface(udev, interface)  < 0)
	{
		printf("Claim interface: %s\n", usb_strerror());
		return -1;
	}

	return 0;
}


/* 
 * function that queries the usb bus and returns a pointer to the correct usb device
 */
struct usb_device * ueib_find_usb_device(int vendor_id, int device_id)
{
	struct usb_device *device = NULL;
	struct usb_bus *bus = NULL;

	/* get all the busses */
	usb_find_busses();

	/* find all the devices */
	usb_find_devices();

	/* loop through all the busses */
	for(bus = usb_busses; bus; bus = bus->next)
	{
		/* loop through all the devices on the bus */
		for (device = bus->devices; device; device = device->next)
		{
			/* check if the device is from the right vendor */
			if(device->descriptor.idVendor == vendor_id)
			{
				/* check if the device is the device where looking for
				 * *we check the device id */
				if(device->descriptor.idProduct == device_id)
				{
					/* if we found the device return a pointer to it */
					return device;
				}
			}
		}
	}
	/* if no device found return NULL */
	return NULL;
}



/********************************************/
/****************** CORE ********************/
/********************************************/

/* update the values, this read's all values and write the set values */
int ueib_update(ueib_handle *uh)
{
	char data[8] = {0,0,0,0,0,0,0};	
	/* check if the struct is not null */
	if(uh == NULL) return -1;

	/* INPUT Section*/
	/* get data from usb device
	 * Use a 30 milisecond timeout, the device is apr. 20 ms and to be on the save side.
	 */
	if(usb_interrupt_read(uh->device_handle,0x81,data,8*sizeof(char),30) < 0)
	{
		printf("Failed to read data\n");
		return -1;
	}

	/* get dig input */
	uh->dig_input = data[0];

	/* get analoge input(s) */
	uh->ana_input_1 = (unsigned char)data[2];
	uh->ana_input_2 = (unsigned char)data[3];

	/* get counters */
	uh->count_1 = (unsigned char) data[4];
	uh->count_2 = (unsigned char) data[6];

	/* OUTPUT Section*/
	/* only write when changes are made. */
	if(uh->output_changed)
	{
		/* somehow it needs to be 5 to be able to write the others */
		data[0] = 5;
		/* digital output */
		data[1] = uh->dig_output;
		/* analog output(s)*/
		data[2] = uh->ana_output_1;
		data[3] = uh->ana_output_2;

		/* 
		 * Use a 30 milisecond timeout, 
		 * the device is apr. 20 ms and to be on the save side.
		 */
		if(usb_interrupt_write(uh->device_handle,0x1,data,8*sizeof(char),30) <0)
		{
			printf("write to usb failed\n");
			return -2;
		}
		uh->output_changed = 0;
	}

	return 0;	
}


/* create a struct and try to get the device */
ueib_handle *ueib_init(int device_id)
{
	struct usb_device *device;
	/* create structure to hold data */
	ueib_handle *uh = malloc(sizeof(ueib_handle));

	/* set some initial values */
	uh->dig_output  = 0;
	uh->ana_output_1 = 0;
	uh->ana_output_2 = 0;
	uh->output_changed = 0;

	/* device must be between 0 and 3 */
	uh->device_id = RANGE(device_id, 0,3);

	/* try to get the device */
	device = ueib_find_usb_device(VENDOR_ID, uh->device_id+BASE_DEVICE_ID);
	if(device == NULL)
	{
		printf("No device found\n");
		free(uh);
		return NULL;
	}
	/* open the device */
	uh->device_handle = usb_open(device);	

	/* check */
	if(uh->device_handle == NULL)
	{
		printf("Failed to open the device\n");
		free(uh);
		return NULL;
	}	
	if (ueib_takeover_device(uh->device_handle,0) < 0)
	{
		printf("Failed to take controll of the device\n");
		free(uh);
		return NULL;
	}

	/* return the structure */
	return uh;
}

/* close and free the stuff */
void ueib_close(ueib_handle *uh)
{
	if(uh == NULL) return;

	if(uh->device_handle != NULL)
	{
		//		usb_release_interface(uh->device_handle, 0);
		usb_close(uh->device_handle);
	}
	free(uh);
}

/********************************************/
/***************** OUTPUT *******************/
/********************************************/

void ueib_set_dig_output(ueib_handle *uh, int bit, int value)
{
	if(uh == NULL) return;
	/* get the current value */
	int cur_val = (uh->dig_output&bit)? 1:0;
	/* cap value */
	value = RANGE(value,0,1);
	/* if not equal toggle the bit */
	if(cur_val != value)
	{
		uh->dig_output = uh->dig_output^bit;
	}
	uh->output_changed = 1;
}

/* you can set analog output 1 or 2 */ 
void ueib_set_ana_output(ueib_handle *uh, int output_number, int value)
{
	if(uh == NULL) return;
	output_number = RANGE(output_number, UEIB_ANA1,UEIB_ANA2);
	value = RANGE(value,0,255);

	if(output_number == UEIB_ANA1)
	{
		uh->ana_output_1 = value;
	}
	else
	{
		uh->ana_output_2 = value;
	}
	uh->output_changed = 1;
}

/* Get the output values, This is the internal value, not the actual value.
 * you need to run ueib_update first to be in sync
 */
int ueib_get_dig_output(ueib_handle *uh, int bit)
{
	if(uh == NULL) return -1;
	return (uh->dig_output&bit)? 1:0;
}
/* get the analoge value, the same goes here as for the dig output */
int ueib_get_ana_output(ueib_handle *uh, int output_number)
{
	if(uh == NULL) return -1;
	if(output_number == UEIB_ANA1)
	{
		return (unsigned short)(uh->ana_output_1) % 256;
	}
	else 
	{
		return (unsigned short)(uh->ana_output_2) % 256;
	}
}


/********************************************/
/****************** INPUT *******************/
/********************************************/

int ueib_get_dig_input(ueib_handle *uh, int bit)
{
	if(uh == NULL) return -1;
	return (uh->dig_input&bit)? 1:0;
}

int ueib_get_ana_input(ueib_handle *uh, int input_number)
{
	if(uh == NULL) return -1;
	if(input_number == UEIB_ANA1)
	{
		return uh->ana_input_1;
	}
	else
	{
		return uh->ana_input_2;
	}
}

int ueib_get_count_input(ueib_handle *uh, int input_number)
{
	if(uh == NULL) return -1;
	if(input_number == UEIB_COUNT1)
	{
		return uh->count_1;
	}
	else
	{
		return uh->count_2;
	}
}

void ueib_reset_count_input(ueib_handle *uh, int input_number)
{
	unsigned char send_code[1] = {3};
	if(uh == NULL) return;
	if(input_number == UEIB_COUNT2)
	{
		send_code[0] = 4;
	}

	usb_interrupt_write(uh->device_handle,0x1,send_code,sizeof(unsigned char),30); 

}
